package com.nowcoder.topic.dp.hard;

import java.util.ArrayList;

/**
 * NC303 龙与地下城游戏问题
 * @author d3y1
 */
public class NC303{
    /**
     * 代码中的类名、方法名、参数名已经指定，请勿修改，直接返回方法规定的值即可
     *
     *
     * @param mp int整型ArrayList<ArrayList<>>
     * @return int整型
     */
    public int dnd (ArrayList<ArrayList<Integer>> mp) {
        return solution(mp);
    }

    /**
     * 动态规划: 注意从右下角往左上角逆序遍历!
     *
     * dp[i][j]表示在位置(i,j)处(第i行第j列)所需要的最少血量
     *
     *            { Math.max(1-mp.get(i-1).get(j-1), 1)                                 , i==n && j==m
     * dp[i][j] = { Math.max(dp[i][j+1]-mp.get(i-1).get(j-1), 1)                        , i==n && j<m
     *            { Math.max(dp[i+1][j]-mp.get(i-1).get(j-1), 1)                        , i<n && j==m
     *            { Math.max(Math.min(dp[i][j+1], dp[i+1][j])-mp.get(i-1).get(j-1), 1)  , i<n && j<m
     *
     * @param mp
     * @return
     */
    private int solution(ArrayList<ArrayList<Integer>> mp){
        int n = mp.size();
        int m = mp.get(0).size();

        int[][] dp = new int[n+1][m+1];

        for(int i=n; i>0; i--){
            for(int j=m; j>0; j--){
                // 在右下角 最终位置
                // 到达位置(i,j)处 遭遇怪兽或血瓶前: 血量至少为1 => 所需最少血量: 1
                // 到达位置(i,j)处 遭遇怪兽或血瓶后: 血量至少为1 => 所需最少血量: 1-mp.get(i-1).get(j-1)
                // 需要同时保证上面2点
                if(i==n && j==m){
                    dp[i][j] = Math.max(1-mp.get(i-1).get(j-1), 1);
                }
                // 最下一行 只能向右
                // 到达位置(i,j)处 遭遇怪兽或血瓶前: 血量至少为1 => 所需最少血量: 1
                // 到达位置(i,j)处 遭遇怪兽或血瓶后: 血量至少为dp[i][j+1] => 所需最少血量: dp[i][j+1]-mp.get(i-1).get(j-1)
                // 需要同时保证上面2点
                else if(i==n && j<m){
                    dp[i][j] = Math.max(dp[i][j+1]-mp.get(i-1).get(j-1), 1);
                }
                // 最右一列 只能向下
                // 到达位置(i,j)处 遭遇怪兽或血瓶前: 血量至少为1 => 所需最少血量: 1
                // 到达位置(i,j)处 遭遇怪兽或血瓶后: 血量至少为dp[i+1][j] => 所需最少血量: dp[i+1][j]-mp.get(i-1).get(j-1)
                // 需要同时保证上面2点
                else if(i<n && j==m){
                    dp[i][j] = Math.max(dp[i+1][j]-mp.get(i-1).get(j-1), 1);
                }
                // 可以向右 可以向下
                // 到达位置(i,j)处 遭遇怪兽或血瓶前: 血量至少为1 => 所需最少血量: 1
                // 到达位置(i,j)处 遭遇怪兽或血瓶后: 血量至少为min(dp[i][j+1], dp[i+1][j]) => 所需最少血量: min(dp[i][j+1], dp[i+1][j])-mp.get(i-1).get(j-1)
                // 需要同时保证上面2点
                else{
                    dp[i][j] = Math.max(Math.min(dp[i][j+1], dp[i+1][j])-mp.get(i-1).get(j-1), 1);
                }
            }
        }

        return dp[1][1];
    }
}